// 报名审核管理

package student

import (
	"api/common"
	"api/common/logger"
	"api/common/orm"
	"api/common/web"
	"api/models"
	"encoding/base64"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"
)

// @TagName 报名审核管理
// @Controller VerifyController
type VerifyController struct{}

// @Title 报名审核列表数据
// @Route /verify/list --> List
// @Method post
func (this *VerifyController) List(w http.ResponseWriter, r *http.Request) {
	defer web.PanicHandler(w, r)
	inputJson, msg := web.TokenCheckRequest(r)
	if msg != nil {
		web.ServeJSON(w, msg)
		return
	}

	model := &models.Info{}
	fields := "id, special_id, type, `name`, `status`, phone, signup, lodging, signup_time"
	where := "(`status`=1 or `status`=2 or `status`=3)"
	order := "`status`, signup_time"
	web.JsonCallBakFilter(w, inputJson, model, fields, where, order)
}

// @Title 报名审核列表数据(返回所有字段)
// @Route /verify/listAll --> ListAll
// @Method post
func (this *VerifyController) ListAll(w http.ResponseWriter, r *http.Request) {
	defer web.PanicHandler(w, r)
	inputJson, msg := web.TokenCheckRequest(r)
	if msg != nil {
		web.ServeJSON(w, msg)
		return
	}

	ids := inputJson["ids"]
	model := &models.Info{}
	where := "(`status`=1 or `status`=2 or `status`=3)"
	if ids != nil && ids != "" {
		where += " and id in (" + common.ToStr(ids) + ")"
	}
	order := "`status`, add_time"
	page, msg := web.JsonRootWithInputFilter(inputJson, model, "", where, order)
	if msg != nil {
		web.ServeJSON(w, msg)
		return
	}

	// 对审核通过的学生记录，附加审核员姓名
	sql := "select id, `name` from basic_user"
	arr := []models.User{}
	conn := orm.NewOrm()
	_, err := conn.Raw(sql).QueryRows(&arr)
	if err != nil {
		logger.Error("查询id与name的映射关系发生错误：", err.Error())
		web.ServeJSON(w, web.JsonMsg{false, err.Error(), 1})
		return
	}

	obj := map[uint32]string{}
	for _, item := range arr {
		obj[item.Id] = item.Name
	}

	for i := 0; i < len(page.Root); i += 1 {
		item := page.Root[i]
		id, _ := strconv.Atoi(item["auditAdmin"].(string))
		page.Root[i]["auditAdminName"] = obj[uint32(id)]
	}

	web.ServeJSON(w, page)
}

// @Title 带统计人数的专业列表数据
// @Route /verify/listSpecial --> ListSpecial
// @Method post
func (this *VerifyController) ListSpecial(w http.ResponseWriter, r *http.Request) {
	defer web.PanicHandler(w, r)
	_, msg := web.TokenCheckRequest(r)
	if msg != nil {
		web.ServeJSON(w, msg)
		return
	}

	data := []map[string]interface{}{}
	sql := "select s.id, s.pid, s.`name`, s.types," +
		" (select count(1) from student_info i where i.`status`=1 and i.special_id=s.id) count" +
		" from basic_special s order by count desc, s.sequ desc"
	conn := orm.NewOrm()
	data, err := conn.Raw(sql).QueryMaps()
	if err != nil {
		logger.Error("查询记录发生错误", err)
		web.ServeJSON(w, web.JsonMsg{false, "查询记录发生错误" + err.Error(), 1})
		return
	}

	result := map[string]interface{}{}
	result["status"] = 0
	result["success"] = true
	result["msg"] = "查询成功"
	result["data"] = data
	web.ServeJSON(w, result)
}

// @Title 学生信息回显
// @Route /verify/echo --> Echo
// @Method post
func (this *VerifyController) Echo(w http.ResponseWriter, r *http.Request) {
	defer web.PanicHandler(w, r)
	msg, _, data := web.EchoModel(w, r, &models.Info{})
	if msg != "" {
		web.ServeJSON(w, web.JsonMsg{false, msg, 1})
		return
	}

	model := data["model"].(*models.Info)
	if model.Status != 2 && model.Status != 5 {
		web.ServeJSON(w, data)
		return
	}

	name := ""
	sql := "select `name` from basic_user where `status` = 1 and id = ?"
	conn := orm.NewOrm()
	err := conn.Raw(sql, model.AuditAdmin).QueryRow(&name)
	if err != nil {
		logger.Error(err)
		web.ServeJSON(w, web.JsonMsg{false, err.Error(), 1})
		return
	}

	data["auditAdminName"] = name
	web.ServeJSON(w, data)
}

// @Title 报名审核通过
// @Route /verify/allow --> Allow
// @Param {"userId":0,"token":"","studentId":0,"feedback":""}
// @Method post
func (this *VerifyController) Allow(w http.ResponseWriter, r *http.Request) {
	defer web.PanicHandler(w, r)
	inputJson, msg := web.TokenCheckRequest(r)
	if msg != nil {
		web.ServeJSON(w, msg)
		return
	}

	userId := inputJson["userId"]
	studentId := inputJson["studentId"]
	feedback := inputJson["feedback"]
	if studentId == nil {
		web.ServeJSON(w, web.JsonMsg{false, "学生id不能为空", 1})
		return
	}
	if feedback == nil || feedback == "" {
		web.ServeJSON(w, web.JsonMsg{false, "反馈信息不能为空", 1})
		return
	}

	user := &models.Info{}
	sql := "select * from student_info where id=?"
	conn := orm.NewOrm()
	err := conn.Raw(sql, studentId).QueryRow(user)
	if err != nil {
		if strings.Index(err.Error(), "no row found") == -1 {
			logger.Info("验证用户信息发生错误", err)
			web.ServeJSON(w, web.JsonMsg{false, "验证用户信息发生错误" + err.Error(), 1})
			return
		}

		web.ServeJSON(w, web.JsonMsg{false, "学生记录不存在", 1})
		return
	}
	if user.Status != 1 {
		//状态: 0已注册待报名, 1已报名待审核, 2审核通过待交费, 3审核被拒, 4已交费待核实, 5已核实待分配, 6已分配
		web.ServeJSON(w, web.JsonMsg{false, "用户状态不正确", 1})
		return
	}

	if user.WeixinId != "" {
		// 检查"操作记录"里是否有依据(是否有前一状态的记录)
		// "WeixinId"不为空表示当前用户是通过微信登录的用户
		// "WeixinId"为空表示是"教师代报"
		count := 0
		sql = "select count(1) from student_operate_record where" +
			" student_id=? and add_time<now() and" +
			" `status`=1 and admin_id=0 and remark=''" +
			" order by add_time desc"
		err = conn.Raw(sql, studentId).QueryRow(&count)
		if err != nil {
			logger.Error("查询操作记录数量发生错误", err)
			web.ServeJSON(w, web.JsonMsg{false, "查询操作记录数量发生错误" + err.Error(), 1})
			return
		}
		if count == 0 {
			web.ServeJSON(w, web.JsonMsg{false, "“操作记录”表里没有前一状态的记录", 1})
			return
		}
	}

	feedbackText := common.ToStr(feedback)
	user.Feedback = feedbackText
	user.Status = 2 // 更新用户状态为: 审核通过待交费
	user.AuditAdmin = uint32(userId.(float64))
	_ = conn.Begin()
	_, err = conn.Update(user)
	if err != nil {
		_ = conn.Rollback()
		logger.Error("更新记录发生错误", err)
		web.ServeJSON(w, web.JsonMsg{false, "更新记录发生错误" + err.Error(), 1})
		return
	}

	rec := &models.OperateRecord{
		StudentId: user.Id,
		AdminId:   uint32(userId.(float64)),
		Status:    2, // 审核通过待交费
	}
	_, err = conn.Insert(rec)
	if err != nil {
		_ = conn.Rollback()
		logger.Error("操作记录入库发生错误", err)
		web.ServeJSON(w, web.JsonMsg{false, "操作记录入库发生错误" + err.Error(), 1})
		return
	}
	_ = conn.Commit()

	if user.WeixinId != "" {
		// 及时通知微信用户审核结果
		sendData := []common.Item{}
		item := common.Item{
			Field: "thing3",
			Value: "报名成功",
		}
		sendData = append(sendData, item)
		item = common.Item{
			Field: "thing5",
			Value: feedbackText,
		}
		sendData = append(sendData, item)

		errMsg := common.SendMsg(
			user.WeixinId,
			common.Config.WX.MessageTemplate.Signup,
			sendData,
		)
		if errMsg != "" {
			web.ServeJSON(w, web.JsonMsg{true, "审核操作成功，但是" + errMsg, 2})
			return
		}
	}

	web.ServeJSON(w, web.JsonMsg{true, "审核操作成功", 0})
}

// @Title 报名审核拒绝
// @Route /verify/deny --> Deny
// @Param {"userId":0,"token":"","studentId":0,"feedback":""}
// @Method post
func (this *VerifyController) Deny(w http.ResponseWriter, r *http.Request) {
	defer web.PanicHandler(w, r)
	inputJson, msg := web.TokenCheckRequest(r)
	if msg != nil {
		web.ServeJSON(w, msg)
		return
	}

	userId := inputJson["userId"]
	studentId := inputJson["studentId"]
	feedback := inputJson["feedback"]
	if studentId == nil {
		web.ServeJSON(w, web.JsonMsg{false, "学生id不能为空", 1})
		return
	}
	if feedback == nil {
		web.ServeJSON(w, web.JsonMsg{false, "拒绝原因不能为空", 1})
		return
	}

	user := &models.Info{}
	sql := "select * from student_info where id=?"
	conn := orm.NewOrm()
	err := conn.Raw(sql, studentId).QueryRow(user)
	if err != nil {
		if strings.Index(err.Error(), "no row found") == -1 {
			logger.Info("验证用户信息发生错误", err)
			web.ServeJSON(w, web.JsonMsg{false, "验证用户信息发生错误" + err.Error(), 1})
			return
		}

		web.ServeJSON(w, web.JsonMsg{false, "学生记录不存在", 1})
		return
	}
	if user.Status != 1 {
		//状态: 0已注册待报名, 1已报名待审核, 2审核通过待交费, 3审核被拒, 4已交费待核实, 5已核实待分配, 6已分配
		web.ServeJSON(w, web.JsonMsg{false, "用户状态不正确", 1})
		return
	}

	if user.WeixinId != "" {
		// 检查"操作记录"里是否有依据(是否有前一状态的记录)
		count := 0
		sql = "select count(1) from student_operate_record where" +
			" student_id=? and add_time<now() and" +
			" `status`=1 and admin_id=0 and remark=''" +
			" order by add_time desc"
		err = conn.Raw(sql, studentId).QueryRow(&count)
		if err != nil {
			logger.Error("查询操作记录数量发生错误", err)
			web.ServeJSON(w, web.JsonMsg{false, "查询操作记录数量发生错误" + err.Error(), 1})
			return
		}
		if count == 0 {
			web.ServeJSON(w, web.JsonMsg{false, "“操作记录”表里没有前一状态的记录", 1})
			return
		}
	}

	feedbackText := common.ToStr(feedback)
	user.Feedback = feedbackText
	user.Status = 3 // 更新用户状态为: 审核被拒
	user.AuditAdmin = uint32(userId.(float64))
	_ = conn.Begin()
	_, err = conn.Update(user)
	if err != nil {
		_ = conn.Rollback()
		logger.Error("更新记录发生错误", err)
		web.ServeJSON(w, web.JsonMsg{false, "更新记录发生错误" + err.Error(), 1})
		return
	}

	rec := &models.OperateRecord{
		StudentId: user.Id,
		AdminId:   uint32(userId.(float64)),
		Remark:    feedbackText,
		Status:    3, // 审核被拒
	}
	_, err = conn.Insert(rec)
	if err != nil {
		_ = conn.Rollback()
		logger.Error("操作记录入库发生错误", err)
		web.ServeJSON(w, web.JsonMsg{false, "操作记录入库发生错误" + err.Error(), 1})
		return
	}
	_ = conn.Commit()

	if user.WeixinId != "" {
		// 及时通知微信用户审核结果
		sendData := []common.Item{}
		item := common.Item{
			Field: "thing3",
			Value: "报名失败",
		}
		sendData = append(sendData, item)
		item = common.Item{
			Field: "thing5",
			Value: feedbackText,
		}
		sendData = append(sendData, item)

		errMsg := common.SendMsg(
			user.WeixinId,
			common.Config.WX.MessageTemplate.Signup,
			sendData,
		)
		if errMsg != "" {
			web.ServeJSON(w, web.JsonMsg{true, "审核操作成功，但是" + errMsg, 2})
			return
		}
	}

	web.ServeJSON(w, web.JsonMsg{true, "审核操作成功", 0})
}

// @Title 调整学生报名信息
// @Route /verify/edit --> Edit
// @Param {"userId":0,"token":"","data":{}}
// @Method post
func (this *VerifyController) Edit(w http.ResponseWriter, r *http.Request) {
	defer web.PanicHandler(w, r)
	inputJson, msg := web.TokenCheckRequest(r)
	if msg != nil {
		web.ServeJSON(w, msg)
		return
	}

	data := inputJson["data"]
	if data == nil {
		web.ServeJSON(w, web.JsonMsg{false, "报名信息不能为空", 1})
		return
	}

	modelMap := data.(map[string]interface{})
	user := &models.Info{}
	web.JsonToStruct(modelMap, user)

	sql := "select * from student_info where id=? and (`status`=2 or `status`=5)"
	conn := orm.NewOrm()
	err := conn.Raw(sql, user.Id).QueryRow(user)
	if err != nil {
		if strings.Index(err.Error(), "no row found") == -1 {
			logger.Info("验证用户信息发生错误", err)
			web.ServeJSON(w, web.JsonMsg{false, "验证用户信息发生错误" + err.Error(), 1})
			return
		}

		web.ServeJSON(w, web.JsonMsg{false, "学生记录不存在", 1})
		return
	}

	web.JsonToStruct(modelMap, user)

	if user.WeixinId != "" {
		// 检查"操作记录"里是否有依据(是否有前一状态的记录)
		count := 0
		sql = "select count(1) from student_operate_record where" +
			" student_id=? and add_time<now() and" +
			" `status`=2 and admin_id>0 and remark=''" +
			" order by add_time desc"
		err = conn.Raw(sql, user.Id).QueryRow(&count)
		if err != nil {
			logger.Error("查询操作记录数量发生错误", err)
			web.ServeJSON(w, web.JsonMsg{false, "查询操作记录数量发生错误" + err.Error(), 1})
			return
		}
		if count == 0 {
			web.ServeJSON(w, web.JsonMsg{false, "“操作记录”表里没有前一状态的记录", 1})
			return
		}
	}

	_, err = conn.Update(user)
	if err != nil {
		logger.Error("更新记录发生错误", err)
		web.ServeJSON(w, web.JsonMsg{false, "更新记录发生错误" + err.Error(), 1})
		return
	}

	web.ServeJSON(w, web.JsonMsg{true, "调整学生信息操作成功", 0})
}

// @Title 删除学生报名信息
// @Route /verify/delete --> Delete
// @Method post
func (this *VerifyController) Delete(w http.ResponseWriter, r *http.Request) {
	defer web.PanicHandler(w, r)
	web.DeleteCallBak(w, r, &models.Info{})
}

// @Title 提交用户报名信息(后台代报)
// @Route /verify/create --> Create
// @Param {"userId":0,"token":"","data":{}}
// @Method post
func (this *VerifyController) Create(w http.ResponseWriter, r *http.Request) {
	defer web.PanicHandler(w, r)
	inputJson, msg := web.TokenCheckRequest(r)
	if msg != nil {
		web.ServeJSON(w, msg)
		return
	}

	userId := inputJson["userId"]
	data := inputJson["data"]
	if data == nil {
		web.ServeJSON(w, web.JsonMsg{false, "报名信息不能为空", 1})
		return
	}

	modelMap := data.(map[string]interface{})
	user := &models.Info{}
	web.JsonToStruct(modelMap, user)

	if user.Phone == "" {
		web.ServeJSON(w, web.JsonMsg{false, "手机号码不能为空", 1})
		return
	}
	if user.IdCardNum == "" {
		web.ServeJSON(w, web.JsonMsg{false, "身份证号码不能为空", 1})
		return
	}

	// 验证"报名信息"的有效性, 防止重复报名
	type Counts struct {
		Phone     uint32
		Idcardnum uint32
	}
	counts := &Counts{}
	sql := "select" +
		" (select count(1) from student_info where phone=?) phone," +
		" (select count(1) from student_info where id_card_num=?) idcardnum"
	conn := orm.NewOrm()
	err := conn.Raw(sql, user.Phone, user.IdCardNum).QueryRow(counts)
	if err != nil {
		logger.Info("验证报名信息的有效性时发生错误", err)
		web.ServeJSON(w, web.JsonMsg{false, "验证报名信息的有效性时发生错误" + err.Error(), 1})
		return
	}
	if counts.Phone > 0 {
		web.ServeJSON(w, web.JsonMsg{false, "手机号码已经存在，请勿重复报名。", 1})
		return
	}
	if counts.Idcardnum > 0 {
		web.ServeJSON(w, web.JsonMsg{false, "身份证号码已经存在，请勿重复报名。", 1})
		return
	}

	user.Id = 0
	user.Status = 2 // 更新用户状态为: 审核通过
	user.AuditAdmin = uint32(userId.(float64))
	_ = conn.Begin()
	_, err = conn.Insert(user)
	if err != nil {
		_ = conn.Rollback()
		logger.Error("新增记录发生错误", err)
		web.ServeJSON(w, web.JsonMsg{false, "新增记录发生错误" + err.Error(), 1})
		return
	}

	rec := &models.OperateRecord{
		StudentId: user.Id,
		AdminId:   user.AuditAdmin,
		Status:    2, // 审核通过
		Remark:    "后台代报 " + user.Recommend,
	}
	_, err = conn.Insert(rec)
	if err != nil {
		_ = conn.Rollback()
		logger.Error("操作记录入库发生错误", err)
		web.ServeJSON(w, web.JsonMsg{false, "操作记录入库发生错误" + err.Error(), 1})
		return
	}
	_ = conn.Commit()

	result := map[string]interface{}{}
	result["status"] = 0
	result["success"] = true
	result["msg"] = "提交报名信息成功"
	result["user"] = user
	web.ServeJSON(w, result)
}

// @Title 上传用户相关图片(后台代报)
// @Route /verify/upload --> Upload
// @Param {"userId":0,"token":"","b64":"base64Code", "b64sm":"base64Code", "imgType":"jpg", "fieldName":"picture"}
// @Method post
func (this *VerifyController) Upload(w http.ResponseWriter, r *http.Request) {
	defer web.PanicHandler(w, r)
	inputJson, msg := web.TokenCheckRequest(r)
	if msg != nil {
		web.ServeJSON(w, msg)
		return
	}

	b64 := inputJson["b64"]
	b64sm := inputJson["b64sm"]
	imgType := inputJson["imgType"]
	fieldName := inputJson["fieldName"]

	if b64 == nil || b64 == "" {
		web.ServeJSON(w, web.JsonMsg{false, "图片数据不能为空", 1})
		return
	}
	if imgType == nil {
		web.ServeJSON(w, web.JsonMsg{false, "图片扩展名不能为空", 1})
		return
	}
	if fieldName == nil {
		web.ServeJSON(w, web.JsonMsg{false, "图片字段名不能为空", 1})
		return
	}

	imgStr, err := base64.StdEncoding.DecodeString(b64.(string))
	if err != nil {
		logger.Error("图片数据不正确", err)
		web.ServeJSON(w, web.JsonMsg{false, "图片数据不正确" + err.Error(), 1})
		return
	}

	// 按"年/月/日"创建文件夹, 防止一个文件夹内文件过多造成读取缓慢
	filePath := "student/" + common.GetNowWithFormat("2006") + "/"
	filePath += common.GetNowWithFormat("01") + "/"
	filePath += common.GetNowWithFormat("02") + "/"

	rand.Seed(time.Now().UnixNano())
	rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
	captcha := fmt.Sprintf("%6v", rnd.Int31n(1000000))

	fileName := captcha + "_" + common.ToStr(fieldName)
	fileUrl := filePath + fileName + "." + common.ToStr(imgType)

	// 绝对路径(防止"MkdirAll"将文件夹创建在"C:\Windows\System32")
	absPath := common.Config.UploadPath
	err = common.CreateChildDir(absPath + filePath)
	if err != nil {
		logger.Error("创建文件夹发生错误", err)
		web.ServeJSON(w, web.JsonMsg{false, "创建文件夹发生错误" + err.Error(), 1})
		return
	}

	file, err := os.Create(absPath + fileUrl)
	if err != nil {
		logger.Error("创建图片文件发生错误", err)
		web.ServeJSON(w, web.JsonMsg{false, "创建图片文件发生错误" + err.Error(), 1})
		return
	}
	_, err = io.WriteString(file, string(imgStr))
	_ = file.Close()
	if err != nil {
		logger.Error("保存图片文件发生错误", err)
		web.ServeJSON(w, web.JsonMsg{false, "保存图片文件发生错误" + err.Error(), 1})
		return
	}

	// 缩略图上传
	if b64sm != nil && b64sm != "" {
		imgStr, err = base64.StdEncoding.DecodeString(b64sm.(string))
		if err != nil {
			logger.Error("缩略图格式不正确", err)
			web.ServeJSON(w, web.JsonMsg{false, "缩略图格式不正确" + err.Error(), 1})
			return
		}

		fileUrl = filePath + fileName + "_sm." + common.ToStr(imgType)
		file, err = os.Create(absPath + fileUrl)
		if err != nil {
			logger.Error("创建缩略图文件发生错误", err)
			web.ServeJSON(w, web.JsonMsg{false, "创建缩略图文件发生错误" + err.Error(), 1})
			return
		}
		_, err = io.WriteString(file, string(imgStr))
		_ = file.Close()
		if err != nil {
			logger.Error("保存缩略图文件发生错误", err)
			web.ServeJSON(w, web.JsonMsg{false, "保存缩略图文件发生错误" + err.Error(), 1})
			return
		}
	}

	result := map[string]interface{}{}
	result["status"] = 0
	result["success"] = true
	result["msg"] = "图片上传成功"
	result["imageUrl"] = fileUrl
	web.ServeJSON(w, result)
}
